#!/usr/bin/env python

import os
import os.path
import sys
import struct
import collections


K4 = 4096
M1 = 1024 * 1024


def f_name(volid, chunk_id):
    return 'raw.%d.%d.dump' % (volid, chunk_id)


def download_raw(volid, chunk_id):
    fname = f_name(volid, chunk_id)
    print '== get chunk', fname
    if os.path.isfile(fname):
        return

    cmd = 'lich.inspect --chunkdump raw.%d.%d' % (volid, chunk_id)
    os.system(cmd)


class ChunkParser(object):
    def __init__(self, volid, chunk_id):
        self.volid = volid
        self.chunk_id = chunk_id

    def read_chunk(self, chunk_id):
        download_raw(self.volid, chunk_id)
        with open(f_name(self.volid, chunk_id), "rb") as f:
            bytes = f.read(M1)
            n = len(bytes)
            assert n == M1
            return bytes

    def run(self):
        download_raw(self.volid, self.chunk_id)
        return self.parse()

    def parse(self):
        pass


class Chunk0Parser(ChunkParser):
    @staticmethod
    def parse_page_wbuf(page):
        total = 16 * 4
        step = 4
        for i in range(0, total, step):
            a = struct.unpack('I', page[i:i+step])
            if a:
                print i, a

    def parse(self):
        with open(f_name(self.volid, self.chunk_id), "rb") as f:
            bytes = f.read(M1)
            n = len(bytes)
            assert n == M1
            print 'wbuf chunk', wbuf_chunk_id
            page0 = bytes[0*K4:1*K4]
            page1 = bytes[1*K4:2*K4]
            page2 = bytes[2*K4:3*K4]
            page3 = bytes[3*K4:4*K4]
            assert len(page0) == K4
            assert len(page1) == K4
            assert len(page2) == K4
            assert len(page3) == K4
            print 'page3', page3
            print
            self.parse_page_wbuf(page3)


class WbufChunkParser(ChunkParser):
    def parse(self):
        with open(f_name(self.volid, self.chunk_id), "rb") as f:
            bytes = f.read(M1)
            n = len(bytes)
            assert n == M1
            page0 = bytes[:K4]
            for i in range(0, K4, 16):
                a, b, c = struct.unpack('LII', page0[i:i+16])
                if a:
                    print a, b, c


class BitmapChunkParser(ChunkParser):

    @staticmethod
    def header_len():
        return (16 + 2 * 4 + 8 + 4 * 4 + 16 * 4 + 4 + 15 * 4 + 256 + 108)

    @staticmethod
    def header_fmt():
        return '16s2ILIIII16II15I256s108s'

    def parse(self):
        with open(f_name(self.volid, self.chunk_id), "rb") as f:
            bytes = f.read(M1)
            n = len(bytes)
            assert n == M1

            step = self.header_len()
            print 'step', step, self.header_fmt()
            t = struct.unpack(self.header_fmt(), bytes[:step])

            chunks = []
            for i in range(0, 10*4, 4):
                off = step+i
                a = struct.unpack('I', bytes[off:off+4])
                b = a[0] & ~(1 << 32)
                chunks.append(b)

            d = {
                'sign': t[0],
                'version': (t[1], t[2]),
                'root': t[3],
                'chunk_count': t[4],
                'active_chunk_id': t[5],
                'first_child_chunk_id': t[6],
                'next_chunk_id': t[7],
                'header_chunk_ids': t[8:24],
                'type_and_flag': t[24],
                'reserved2': t[25:40],
                'name': t[40],
                'reserved3': t[41],
                'chunks': chunks,
            }
            print 'bitmap header:'
            for k, v in d.iteritems():
                print '\t', k, v
            return d

    def parse_bitmap(self, lba_d, chunk_id, bitmap_off):
        bytes = self.read_chunk(chunk_id)
        lba_base = bitmap_off << 29
        print 'lba_base', lba_base
        count = 0
        for i in range(0, M1, 8):
            x, y = struct.unpack('II', bytes[i:i+8])
            if x:
                lba = lba_base + count*K4
                if lba not in lba_d:
                    lba_d[lba] = []
                lba_d[lba].append((x, y))
            count += 1

    def read_bitmap(self):
        d = self.run()
        lba_d = {}
        for bitmap_off, chunk_id in enumerate(d['chunks']):
            if chunk_id:
                self.parse_bitmap(lba_d, chunk_id, bitmap_off)

        od = collections.OrderedDict(sorted(lba_d.items()))
        for k, v in od.iteritems():
            print 'lba from bitmap', k, v

        return lba_d

    def get_logs(self):
        chunks = []
        d = self.read_bitmap()
        for k, v in d.iteritems():
            x, y = v[0]
            if x not in chunks:
                chunks.append(x)
        chunks.sort()
        return chunks

    def parse_log(self, d, chunk_id):
        bytes = self.read_chunk(chunk_id)
        count = 1
        for i in range(0, K4, 16):
            if i == 0:
                continue
            lba, size, crc = struct.unpack('LII', bytes[i:i+16])
            if lba not in d:
                d[lba] = []
            # print lba, size, crc
            d[lba].append((chunk_id, count * K4))
            count += 1

    def read_log(self):
        logs = self.get_logs()
        min_chunk_id = logs[0]
        max_chunk_id = logs[len(logs) - 1]

        d = {}

        for i in range(max_chunk_id, min_chunk_id - 1, -1):
            self.parse_log(d, i)

        od = collections.OrderedDict(sorted(d.items()))
        for k, v in od.iteritems():
            print 'lba from log', k, v

        return d

    def diff_bitmap(self):
        bitmap_d = parser.read_bitmap()
        # print parser.get_logs()
        log_d = parser.read_log()

        print len(bitmap_d)
        print len(log_d)

        od = collections.OrderedDict(sorted(log_d.items()))
        for k, v in od.iteritems():
            if k not in bitmap_d:
                print 'error bitmap', k, v
                continue
            log_pa = v[0]
            bit_pa = bitmap_d[k][0]
            if log_pa[0] != bit_pa[0] or log_pa[1] != bit_pa[1]:
                print 'error pa', log_pa, bit_pa


if __name__ == '__main__':
    for x in sys.argv:
        # print x
        pass

    g_vol_id = int(sys.argv[1])
    if len(sys.argv) > 2:
        wbuf_chunk_id = int(sys.argv[2])
    else:
        wbuf_chunk_id = 21

    # parser = Chunk0Parser(g_vol_id, 0)
    # parser.run()

    parser = BitmapChunkParser(g_vol_id, 2)
    parser.diff_bitmap()
    # parser.run()

    # for chunk_id in range(5, 21):
    #    parser = WbufChunkParser(g_vol_id, chunk_id)
    #    parser.run()
